package healthylife.top.sync;

import cn.hutool.core.io.IoUtil;
import cn.hutool.json.JSONUtil;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author maomao
 */
@Slf4j
@Data
public class LocalFileClient implements FileSyncService {

    @Override
    public String getSource() {
        return this.source;
    }

    LocalFileClient(String source, String target, String workSpace) {
        this.source = source;
        this.target = target;
        this.workspace = workSpace;
    }

    public Map<String, FileInfo> lastTransferMap;
    public Map<String, FileInfo> sourceFilesMap;
    String source;
    String target;
    String workspace;

    @Override
    @SneakyThrows
    public List<FileInfo> sourceFiles() {
        List<FileInfo> fileInfos = new ArrayList<>();
        Files.walk(Paths.get(source))
                .filter(Files::isRegularFile)
                .forEach(path -> {
                    String relativePath = Paths.get(source).relativize(path).toString();
                    FileInfo fileInfo = new FileInfo();
                    fileInfo.setFilePath(relativePath);
                    fileInfo.setFileId(path.toFile().getAbsolutePath());
                    fileInfo.setSize(path.toFile().length());
                    fileInfo.setDirectory(path.toFile().isDirectory());
                    fileInfo.setLastModifiedTime(path.toFile().lastModified());
                    fileInfos.add(fileInfo);
                });
        sourceFilesMap = fileInfos.stream().collect(Collectors.toMap(FileInfo::getFilePath, Function.identity()));
        return fileInfos;
    }

    @Override
    @SneakyThrows
    public List<FileInfo> targetFiles() {
        List<FileInfo> fileInfos = new ArrayList<>();
        // 遍历本地目录，支持嵌套目录
        Path root = Paths.get(target);
        File file = root.toFile();
        if (!file.exists()) {
            Files.createDirectories(root);
        }
        Files.walk(root)
                .filter(Files::isRegularFile)
                .forEach(path -> {
                    String relativePath = Paths.get(target).relativize(path).toString();
                    FileInfo fileInfo = new FileInfo();
                    fileInfo.setFilePath(relativePath);
                    fileInfo.setDirectory(path.toFile().isDirectory());
                    fileInfo.setLastModifiedTime(path.toFile().lastModified());
                    fileInfos.add(fileInfo);
                });
        return fileInfos;
    }

    @Override
    @SneakyThrows
    public int transfer(String source, String target) {
        String localPath = this.source + File.separator + source;
        String targetFilePath = this.target + File.separator + source;
        Path path = Paths.get(localPath);
        if (!Files.exists(path)) {
            throw new FileNotFoundException(source);
        }
        File file = path.toFile();
        FileInputStream fileInputStream = new FileInputStream(file);
        byte[] bytes = IoUtil.readBytes(fileInputStream);
        Path targetPath = Paths.get(targetFilePath);
        if (!Files.exists(targetPath)) {
            try {
                Path parent = targetPath.getParent();
                if (parent != null && !Files.exists(parent)) {
                    Files.createDirectories(parent);
                }
                FileOutputStream fileOutputStream = new FileOutputStream(targetFilePath);
                IoUtil.write(fileOutputStream, true, bytes);
                log.info("同步文件成功[新增]:{}", target);
                return 0;
            } catch (Exception e) {
                log.error("目标文件创建失败:{}", e.getMessage());
            }
        }
        if (file.isDirectory()) {
            return -1;
        }
        if (!sourceFilesMap.containsKey(source)) {
            return -1;
        }
        long lastModifiedTime = sourceFilesMap.get(source).getLastModifiedTime();
        if (needTransfer(source, lastModifiedTime)) {
            FileOutputStream fileOutputStream = new FileOutputStream(targetFilePath);
            IoUtil.write(fileOutputStream, true, bytes);
            log.info("同步文件成功[更新]:{}", target);
            return 1;
        }
        log.info("已存在,无需同步:{}", target);
        return -1;
    }

    @SneakyThrows
    private boolean needTransfer(String relativeFilePath, long currentFileLastModifiedTime) {
        if (lastTransferMap == null) {
            Path path = Paths.get(getConf());
            File file = path.toFile();
            if (!file.exists()) {
                log.warn("同步文件配置信息不存在");
                return true;
            }
            String json = IoUtil.read(Files.newInputStream(file.toPath()), Charset.defaultCharset());
            List<FileInfo> list = JSONUtil.toList(json, FileInfo.class);
            lastTransferMap = list.stream().collect(Collectors.toMap(FileInfo::getFilePath, Function.identity()));
        }
        if (lastTransferMap.containsKey(relativeFilePath)) {
            boolean b = lastTransferMap.get(relativeFilePath).getLastModifiedTime() != currentFileLastModifiedTime;
            if (b) {
                log.info("文件:{}有更新", relativeFilePath);
                return true;
            } else {
                return false;
            }
        }
        return true;
    }

    @Override
    @SneakyThrows
    public void deleteFile(String filePath) {
        String targetPath = this.getTarget() + File.separator + filePath;
        // 删除本地文件
        Path path = Paths.get(targetPath);
        Files.deleteIfExists(path);
        // 删除空目录
        deleteEmptyParentDirectories(path.getParent());
    }

    private static void deleteEmptyParentDirectories(Path directory) throws IOException {
        if (directory == null || !Files.exists(directory)) {
            return;
        }
        // 如果目录为空，删除它
        if (Files.isDirectory(directory) && Objects.requireNonNull(directory.toFile().list()).length == 0) {
            Files.delete(directory);

            // 递归删除上级空目录
            deleteEmptyParentDirectories(directory.getParent());
        }
    }

}
